package com.biz.primus.ms.promotion.dao.redis;

import com.biz.primus.base.enums.PromotionTypeEnum;
import com.biz.primus.base.redis.CrudRedisDao;
import com.biz.primus.base.redis.util.RedisUtil;
import com.biz.primus.common.utils.ValueUtils;
import com.biz.primus.model.promotion.exception.PromotionGainLimitCountDownTimesException;
import com.biz.primus.ms.promotion.dao.ro.PromotionGainLimitRo;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

/**
 * Promotion Gain Limit Redis Dao
 *
 * @author david-liu
 */
@Repository
@Slf4j
public class PromotionGainLimitRedisDao extends CrudRedisDao<PromotionGainLimitRo, Long> {

    /**
     * 标记一个会员已经参与过某个促销
     *
     * @param memberId 会员ID
     * @param promotionId 促销ID
     */
    public void markedMemberHasGainedPromotion(Long memberId, Long promotionId) {
        super.set(this.getMemberGainedPromotionStringKey(memberId, promotionId), RedisUtil.toByteArray(true));
    }

    /**
     * 过滤传入的活动ID, 返回用户没有参与的活动ID
     */
    public List<Long> filterMemberHasGainedPromotion(Long memberId, List<Long> promotionIds) {
        List<String> memberGainedKeys = Lists.newArrayList();
        for (Long promotionId : promotionIds) {
            memberGainedKeys.add(this.getMemberGainedPromotionStringKey(memberId, promotionId));
        }
        List<Object> list = Optional.ofNullable(super.pipeGet(memberGainedKeys)).orElse(Collections.emptyList());
        List<Long> memberHasNotGainedPromotions = Collections.emptyList();
        for (int i = 0; i < list.size(); i++) {
            Boolean hasMemberGained = RedisUtil.byteArrayToBoolean((byte[]) list.get(i));
            if (Objects.isNull(hasMemberGained) || !hasMemberGained) {
                memberHasNotGainedPromotions.add(promotionIds.get(i));
            }
        }
        return memberHasNotGainedPromotions;
    }

    /**
     * 扣减一次促销可生效次数
     *
     * @param promotionId 活动ID
     * @param type 促销类型
     * @return boolean
     */
    public Boolean decreasePromotionEffectTimes(Long promotionId, PromotionTypeEnum type) throws PromotionGainLimitCountDownTimesException {
        try {
            String promtoionEffectTimesStringKey = this.getPromtoionEffectTimesStringKey(promotionId, type.name());
            Long postIncrVal = super.incrby(promtoionEffectTimesStringKey, (long) -1);
            if (postIncrVal < 0) {
                super.incrby(promtoionEffectTimesStringKey, (long) 1);
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new PromotionGainLimitCountDownTimesException(e.getMessage());
        }
    }

    /**
     * 增加一次促销可生效次数
     *
     * @param promotionId 活动ID
     * @param type 促销类型
     * @return boolean
     */
    public Boolean increasePromotionEffectTimes(Long promotionId, PromotionTypeEnum type) {
        String promtoionEffectTimesStringKey = this.getPromtoionEffectTimesStringKey(promotionId, type.name());
        return ValueUtils.getValue(super.incrby(promtoionEffectTimesStringKey, (long) 1)) > 0;
    }

    /**
     * 判断用户是否已经参加了某个促销活动
     *
     * @param memberId 会员ID
     * @param promotionId 促销ID
     * @return boolean
     */
    public boolean hasMemberGainedPromotion(Long memberId, Long promotionId) {
        Boolean hasGained = RedisUtil.byteArrayToBoolean(super.get(this.getMemberGainedPromotionStringKey(memberId, promotionId)));
        return Objects.nonNull(hasGained) && hasGained;
    }

    /**
     * 获取标记用户已经参加过某个促销的Redis String key
     *
     * @param memberId 会员ID
     * @param promotionId 促销ID
     * @return Redis String key
     */
    private String getMemberGainedPromotionStringKey(Long memberId, Long promotionId) {
        return super.getKeyByParams("member", memberId, "pmt", promotionId);
    }

    /**
     * 获取促销可生效次数String Key
     *
     * @param promotionId 活动ID
     * @param type 活动类型
     * @return String Key
     */
    private String getPromtoionEffectTimesStringKey(Long promotionId, String type) {
        return super.getKeyByParams(promotionId, type, "times");
    }
}
